home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 20 / Cream of the Crop 20 (Terry Blount) (1996).iso / program / commio0b.zip / MTASK.PAS < prev    next >
Pascal/Delphi Source File  |  1996-05-06  |  13KB  |  510 lines

  1. UNIT mtask;
  2.  
  3. {MTASK 2.0, a simple multi-tasker unit for Turbo Pascal 5.
  4.  
  5. Written in November, 1988, and donated to the public domain by:
  6.  
  7.    Wayne E. Conrad
  8.    2627 North 51st Ave, #219
  9.    Phoenix, AZ  85035
  10.    BBS: (602) 484-9356, 300/1200/2400, 24 hours/day
  11.  
  12. This unit provides Turbo Pascal 5 with what I call "request driven"
  13. multi-tasking.  Switching from the current task to another task is done
  14. whenever the current task requests a task switch by calling procedure
  15. "switch_task."  No interrupt driven context switching is done, because
  16. it's a hassle.}
  17.  
  18.  
  19. {$F+}  {Most procedures in this unit must be FAR}
  20.  
  21.  
  22. INTERFACE
  23.  
  24.  
  25. {Result codes.  0 is "no error"}
  26.  
  27. CONST
  28.   heap_full       = 1;   {Unable to allocate heap for the task's stack}
  29.   too_many_tasks  = 2;   {Maximum number of tasks are already running}
  30.   invalid_task_id = 3;   {There is no task with that ID number}
  31.  
  32.  
  33. {This is the procedure type for a task.  The parent task can pass any
  34. type of variable to  the child task.}
  35.  
  36. TYPE
  37.   task_proc = PROCEDURE (VAR param);
  38.  
  39.  
  40. {See the IMPLEMENTATION section for descriptions of these procedures and
  41. functions.}
  42.  
  43. PROCEDURE create_task
  44.   (
  45.   task      : task_proc;
  46.   VAR param ;
  47.   stack_size: Word;
  48.   VAR id    : Word;
  49.   VAR result: Word
  50.   );
  51. PROCEDURE terminate_task (id: Word; VAR result: Word);
  52. PROCEDURE switch_task;
  53. FUNCTION current_task_id: Word;
  54. FUNCTION number_of_tasks: Word;
  55.  
  56. {The maximum number of tasks.  Modify to suit your needs.}
  57. CONST
  58.   max_tasks = 32;
  59.  
  60. IMPLEMENTATION
  61.  
  62.  
  63.  
  64. {This record contains all the information about a task, as follows:
  65.  
  66.   stack_ptr:   Saved stack segment (ss) and stack pointer (sp) registers
  67.  
  68.   stack_org:   If the stack is stored on the heap, this is the address of
  69.                the beginning of the block of memory allocated for the stack.
  70.  
  71.   stack_bytes: Size of stack on the heap, or 0 if the stack is not on the
  72.                heap.  If the stack is not on the heap, then this field is 0.
  73.  
  74.   bp:          Saved value of base pointer (BP) register.
  75.  
  76.   id:          The id number of the task
  77.  
  78. Note that DS (Data Segment register) is not stored.  We can get away with
  79. this by assuming that all tasks will use the same data segment.}
  80.  
  81. TYPE
  82.   task_rec =
  83.     RECORD
  84.     stack_ptr  : Pointer;
  85.     stack_org  : Pointer;
  86.     stack_bytes: Word;
  87.     bp         : Word;
  88.     id         : Word;
  89.     END;
  90.  
  91.  
  92. {The number of tasks in the system}
  93.  
  94. VAR
  95.   ntasks: Word;
  96.  
  97.  
  98. {Information for each task.}
  99.  
  100. VAR
  101.   task_info: ARRAY [1..max_tasks] OF task_rec;
  102.  
  103.  
  104. {The last task ID assigned.  If we haven't rolled the id's over, then
  105. this allows us to assign task ID's without checking to see what id's have
  106. been assigned.}
  107.  
  108. VAR
  109.   last_id    : Word;
  110.   id_rollover: Boolean;
  111.  
  112.  
  113. {This is the task number of the currently executing task}
  114.  
  115. VAR
  116.   current_task: Word;
  117.  
  118.  
  119. {This is the record type of the initial contents of the stack when a task
  120. is created.  When the task is first switched to, it will be from within
  121. the switch_task, terminate_task, or terminate_current_task procedure.  At
  122. the end of switch_task, BP will be popped, then a far return will be
  123. done.  The far return will transfer to the beginning of task.  The task
  124. can access the parameter "task_param," which is a pointer to whatever
  125. data structure that the creator of this task wanted to pass to the new
  126. task.  When the task finally exits, a far return to "end_task" will be
  127. done.  The exception is the main task, which ends the program completely
  128. if it exits.}
  129.  
  130. TYPE
  131.   initial_stack_rec_ptr = ^initial_stack_rec;
  132.   initial_stack_rec =
  133.     RECORD
  134.     bp        : Word;
  135.     task_addr : task_proc;
  136.     end_task  : Pointer;
  137.     task_param: Pointer;
  138.     END;
  139.  
  140.  
  141. {Given a task ID, return the task number, or 0 if there is no task with
  142. that ID.}
  143.  
  144. FUNCTION find_task (target_id: Word): Word;
  145. VAR
  146.   n: Word;
  147. BEGIN
  148.   n := 1;
  149.   WHILE (n <= ntasks) AND (task_info [n].id <> target_id) DO
  150.     Inc (n);
  151.   IF (n > ntasks) THEN
  152.     n := 0;
  153.   find_task := n
  154. END;
  155.  
  156.  
  157. {Remove a task's information from the task info array, and decrement the
  158. number of tasks.}
  159.  
  160. PROCEDURE delete_task_info (task_num: Word);
  161. VAR
  162.   i: Word;
  163. BEGIN
  164.   FOR i := task_num TO ntasks - 1 DO
  165.     task_info [i] := task_info [i + 1];
  166.   Dec (ntasks)
  167. END;
  168.  
  169.  
  170. {Terminate the current task.  If the current task is the only task, then
  171. the program is halted.  If the current task's stack was allocated from
  172. the heap, it is freed.}
  173.  
  174. PROCEDURE terminate_current_task;
  175.  
  176.  
  177. {These are defined as constants to force them into the data segment.
  178. They can't be local, because local variables are stored on the stack and
  179. we're going to switch to a different task (and therefore to a different
  180. stack) before we're done with these variables.}
  181.  
  182. CONST
  183.   old_stack_org  : Pointer = NIL;
  184.   old_stack_bytes: Word = 0;
  185.  
  186.  
  187. VAR
  188.   task_num : Word;
  189.   new_stack: Pointer;
  190.   new_bp   : Word;
  191.  
  192.  
  193. BEGIN {terminate_current_task}
  194.  
  195.   {If we're the last task left, then exit to DOS}
  196.  
  197.   IF ntasks <= 1 THEN
  198.     Halt;
  199.  
  200.   {Remember where the task's stack is so that we can free it up if it's
  201.   on the heap.  We can't free it now, because we're still using it!}
  202.  
  203.   WITH task_info [current_task] DO
  204.     BEGIN
  205.     old_stack_org   := stack_org;
  206.     old_stack_bytes := stack_bytes
  207.     END;
  208.  
  209.   {Remove the task's information from the task info array}
  210.  
  211.   delete_task_info (current_task);
  212.   IF current_task > ntasks THEN
  213.     current_task := 1;
  214.  
  215.   {Switch to the next task.  The stack_ptr and bp are transfered into
  216.   local variables because it's much easier to access simple variables in
  217.   INLINE code than it is to access array variables.}
  218.  
  219.   WITH task_info [current_task] DO
  220.     BEGIN
  221.     new_stack := stack_ptr;
  222.     new_bp    := bp
  223.     END;
  224.   INLINE
  225.     (
  226.     $8b/$86/>new_stack+0/     {MOV  AX,[BP].NEW_STACK+0}
  227.     $8b/$96/>new_stack+2/     {MOV  DX,[BP].NEW_STACK+2}
  228.     $8b/$ae/>new_bp/          {MOV  BP,[BP].NEW_BP}
  229.     $fa/                      {CLI}
  230.     $8e/$d2/                  {MOV  SS,DX}
  231.     $8b/$e0/                  {MOV  SP,AX}
  232.     $fb                       {STI}
  233.     );
  234.  
  235.   {If the task we just got rid of had its heap on the stack, then release
  236.   that memory back to the free pool.}
  237.  
  238.   IF old_stack_bytes > 0 THEN
  239.     FreeMem (old_stack_org, old_stack_bytes)
  240.  
  241. END;
  242.  
  243.  
  244. {Terminate a task.  If task_id is 0, then the current task is deleted.
  245. Possible result codes are:
  246.  
  247.   0                   No error
  248.   invalid_task_id     There is no task with that ID number}
  249.  
  250. PROCEDURE terminate_task (id: Word; VAR result: Word);
  251.  
  252.  
  253.   {Delete a task.  Do not use to delete the current task!}
  254.  
  255.   PROCEDURE delete_task (task_num: Word);
  256.   BEGIN
  257.     WITH task_info [task_num] DO
  258.       IF stack_bytes > 0 THEN
  259.         FreeMem (stack_org, stack_bytes);
  260.     delete_task_info (task_num);
  261.     IF current_task > task_num THEN
  262.       Dec (current_task)
  263.   END;
  264.  
  265.  
  266. VAR
  267.   task_num: Word;
  268.  
  269. BEGIN {terminate_task}
  270.   result := 0;
  271.   IF id = 0 THEN
  272.     terminate_current_task
  273.   ELSE
  274.     BEGIN
  275.     task_num := find_task (id);
  276.     IF task_num = 0 THEN
  277.       result := invalid_task_id
  278.     ELSE
  279.       IF task_num = current_task THEN
  280.         terminate_current_task
  281.       ELSE
  282.         delete_task (task_num)
  283.     END
  284. END;
  285.  
  286.  
  287. {Create a new task and pass parameter "param" to it.  Stack space for the
  288. task is allocated from the heap, and the stack is initialized so that
  289. procedure "new_task" will be executed with parameter "param".  Result
  290. codes are:
  291.  
  292.   0                  No error occured
  293.   heap_full          Unable to allocate heap for the task's stack
  294.   too_many_tasks     Maximum number of tasks are already running
  295.  
  296. If an error occurs, then id is not set.  Otherwise, id is the task id of
  297. the newly created task.}
  298.  
  299. PROCEDURE create_task
  300.   (
  301.   task      : task_proc;
  302.   VAR param ;
  303.   stack_size: Word;
  304.   VAR id    : Word;
  305.   VAR result: Word
  306.   );
  307.  
  308.  
  309. {This is the task number of the task we're creating}
  310.  
  311. VAR
  312.   task_num: Word;
  313.  
  314.  
  315.   {Allocate stack space for the task.  The minimum allowable requested
  316.   stack size is 512 bytes.  For